﻿Public Class CDataDiff_PkLong
	'Inputs
	Public SourceDb As CDataSrc
	Public TargetDb As CDataSrc
	Public TableName As String
	Public PrimaryKey As String

	'Data
	Public SourceTable As DataTable
	Public TargetTable As DataTable

	'PK-Index/Diff
	Public SourcePkIndex As Dictionary(Of Long, DataRow)
	Public TargetPkIndex As Dictionary(Of Long, DataRow)
	Public DiffOnPk As CDiff_Long

	'Data-Hashing Index/Diff
	Public SourceRowHashIndex As Dictionary(Of Guid, List(Of Long))
	Public TargetRowHashIndex As Dictionary(Of Guid, List(Of Long))
	Public DiffOnRowHash As CDiff_Guid

	'Exact match rows
	Public ExactMatchPks As List(Of Long)

	'Used for Resolve Method
	Public SourcePk2HashIndex As Dictionary(Of Long, Guid)
	Public TargetPk2HashIndex As Dictionary(Of Long, Guid)


	'Constructor
	Public Sub New(source As CDataSrc, target As CDataSrc, tableName As String)
		If Not tableName.Contains("[") Then tableName = String.Concat("[", tableName.Replace(".", "].["), "]")

		Me.SourceDb = source
		Me.TargetDb = target
		Me.TableName = tableName

		'Examine
		Me.SourceTable = source?.SelectAll_Dataset(tableName)?.Tables(0)
		Me.TargetTable = target?.SelectAll_Dataset(tableName)?.Tables(0)

		'Index/Diff By PK
		Me.PrimaryKey = SourceTable?.Columns(0)?.ColumnName
		Me.SourcePkIndex = CDiffLogic.TableIndex_PkLong(Me.SourceTable)
		Me.TargetPkIndex = CDiffLogic.TableIndex_PkLong(Me.TargetTable)
		Me.DiffOnPk = CDiffLogic.Diff_PKLong(Me.SourcePkIndex, Me.TargetPkIndex)

		'Index/Diff By Md5
		Me.SourceRowHashIndex = CDiffLogic.RowHashing_PkLong(SourceTable)
		Me.TargetRowHashIndex = CDiffLogic.RowHashing_PkLong(TargetTable, SourceTable)
		Me.DiffOnRowHash = CDiffLogic.Diff_MD5(Me.SourceRowHashIndex, Me.TargetRowHashIndex)

		'Union of pk/md5 matching
		Me.ExactMatchPks = CDiffLogic.Diff_Exact(Me)

		'Used for Resolve Method
		Me.SourcePk2HashIndex = CDiffLogic.ReverseHash_PKLong(SourceRowHashIndex)
		Me.TargetPk2HashIndex = CDiffLogic.ReverseHash_PKLong(TargetRowHashIndex)
	End Sub

	Public ReadOnly Property SourceOnlyRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(SourceTable, DiffOnRowHash.SourceOnly, SourceRowHashIndex, SourcePkIndex)
		End Get
	End Property
	Public ReadOnly Property TargetOnlyRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(TargetTable, DiffOnRowHash.TargetOnly, TargetRowHashIndex, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property MatchingRows_ByMD5 As DataTable
		Get
			Return GuidsToDataTable(TargetTable, DiffOnRowHash.Matching, TargetRowHashIndex, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property SourceOnlyRows_ByPk As DataTable
		Get
			Return PKsToDataTable(SourceTable, DiffOnPk.SourceOnly, SourcePkIndex)
		End Get
	End Property
	Public ReadOnly Property TargetOnlyRows_ByPk As DataTable
		Get
			Return PKsToDataTable(TargetTable, DiffOnPk.TargetOnly, TargetPkIndex)
		End Get
	End Property
	Public ReadOnly Property MatchingRows_ByPk As DataTable
		Get
			Return PKsToDataTable(TargetTable, DiffOnPk.Matching, TargetPkIndex)
		End Get
	End Property


	Private Shared Function PKsToDataTable(original As DataTable, pks As List(Of Long), pkIndex As Dictionary(Of Long, DataRow)) As DataTable
		Dim dt As New DataTable()
		For Each i As DataColumn In original.Columns
			dt.Columns.Add(i.ColumnName, i.DataType)
		Next

		For Each i As DataRow In PKsToRows(pks, pkIndex)
			dt.ImportRow(i)
		Next
		Return dt
	End Function
	Private Shared Function PKsToRows(pks As List(Of Long), pkIndex As Dictionary(Of Long, DataRow)) As List(Of DataRow)

		Dim list As New List(Of DataRow)
		For Each i As Long In pks
			list.Add(pkIndex(i))
		Next
		Return list
	End Function

	Private Shared Function GuidsToDataTable(original As DataTable, guids As List(Of Guid), hashIndex As Dictionary(Of Guid, List(Of Long)), pkIndex As Dictionary(Of Long, DataRow)) As DataTable
		Dim dt As New DataTable()
		For Each i As DataColumn In original.Columns
			dt.Columns.Add(i.ColumnName, i.DataType)
		Next

		For Each i As DataRow In GuidsToRows(guids, hashIndex, pkIndex)
			dt.ImportRow(i)
		Next
		Return dt
	End Function
	Private Shared Function GuidsToRows(guids As List(Of Guid), hashIndex As Dictionary(Of Guid, List(Of Long)), pkIndex As Dictionary(Of Long, DataRow)) As List(Of DataRow)

		Dim list As New List(Of DataRow)
		For Each i As Guid In guids
			Dim pks As List(Of Long) = hashIndex(i)
			For Each j As Long In pks
				list.Add(pkIndex(j))
			Next
		Next
		Return list
	End Function

	'Resolver (fix data)
	Public Sub Resolve(isIdentity As Boolean)
		'Delete ids not in source
		For Each i As Long In DiffOnPk.TargetOnly
			TargetDb.Delete(New CWhere(TableName, New CCriteria(PrimaryKey, i), Nothing))
		Next

		'update common ids
		Dim colS As DataColumnCollection = SourceTable.Columns
		For Each i As Long In DiffOnPk.Matching
			Dim s As Guid = SourcePk2HashIndex(i)
			Dim t As Guid = TargetPk2HashIndex(i)
			If s = t Then Continue For

			Dim dr As DataRow = SourcePkIndex(i)
			Dim nv As New CNameValueList(colS.Count - 1)
			For Each j As DataColumn In colS
				If j.ColumnName = PrimaryKey Then Continue For
				If j.ColumnName = "__hash__" Then Continue For
				If j.ColumnName = "TimeStamp" Then Continue For
				If dr.IsNull(j) Then Continue For
				nv.Add(j.ColumnName, dr(j))
			Next

			TargetDb.Update(nv, New CWhere(TableName, New CCriteria(PrimaryKey, i), Nothing))
		Next

		'insert new ids
		Dim cn As IDbConnection = TargetDb.Local.Connection
		Dim tx As IDbTransaction = cn.BeginTransaction()
		Try

			If isIdentity Then TargetDb.SetIdentityInsert(True, TableName, tx)
			For Each i As Long In DiffOnPk.SourceOnly
				Dim dr As DataRow = SourcePkIndex(i)
				Dim nv As New CNameValueList(colS.Count - 1)
				For Each j As DataColumn In colS
					If j.ColumnName = "__hash__" Then Continue For
					If j.ColumnName = "TimeStamp" Then Continue For
					If dr.IsNull(j) Then Continue For
					nv.Add(j.ColumnName, dr(j))
				Next

				Dim id As Object = TargetDb.Insert(TableName, PrimaryKey, True, nv, tx, Nothing)
			Next
			If isIdentity Then TargetDb.SetIdentityInsert(True, TableName, tx)

			tx.Commit()
		Catch ex As Exception
			tx.Rollback()
			Throw ex
		Finally
			cn.Close()
		End Try

		CCache.ClearAll()
	End Sub
	Public Sub ResolveByHash()
		'Delete hashes not in source
		For Each i As Guid In DiffOnRowHash.TargetOnly
			TargetDb.Delete(New CWhere(TableName, New CCriteria(PrimaryKey, ESign.IN, Me.TargetRowHashIndex(i)), Nothing))
		Next

		'insert  hashes only in source
		Dim colS As DataColumnCollection = SourceTable.Columns
		For Each i As Guid In DiffOnRowHash.SourceOnly
			For Each j As Long In SourceRowHashIndex(i)
				Dim dr As DataRow = SourcePkIndex(j)

				Dim nv As New CNameValueList(colS.Count - 1)
				For Each k As DataColumn In colS
					If k.ColumnName = "__hash__" Then Continue For
					If k.ColumnName = "TimeStamp" Then Continue For
					nv.Add(k.ColumnName, dr(k))
				Next
				nv.RemoveAt(0)

				Dim id As Object = TargetDb.Insert(TableName, PrimaryKey, True, nv, Nothing, Nothing)

			Next
		Next


		CCache.ClearAll()
	End Sub
End Class
